package com.wcs.exam.controller.admin.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.Result;
import com.wcs.exam.common.base.exception.BaseException;
import com.wcs.exam.common.base.page.Page;
import com.wcs.exam.common.base.page.PageUtil;
import com.wcs.exam.common.constant.RedisConstant;
import com.wcs.exam.common.enums.CategoryTypeEnum;
import com.wcs.exam.common.enums.CiteResourceTypeEnum;
import com.wcs.exam.common.enums.PublishStatusEnum;
import com.wcs.exam.common.enums.QuestionTypeEnum;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.controller.admin.req.*;
import com.wcs.exam.controller.admin.resp.AdminPaperCiteListResp;
import com.wcs.exam.controller.admin.resp.AdminPaperDetailViewResp;
import com.wcs.exam.controller.admin.resp.AdminPaperPageResp;
import com.wcs.exam.controller.admin.resp.AdminPaperViewResp;
import com.wcs.exam.controller.biz.CategoryCommonBiz;
import com.wcs.exam.controller.biz.PaperCommonBiz;
import com.wcs.exam.controller.biz.QuestionCommonBiz;
import com.wcs.exam.controller.biz.resp.QuestionDetailResp;
import com.wcs.exam.dao.*;
import com.wcs.exam.dao.impl.mapper.entity.*;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Admin-试卷信息
 *
 * @author wcs
 * @since 2025-03-11
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AdminPaperBiz extends BaseBiz {

    @NotNull
    private final RedissonClient redissonClient;

    @NotNull
    private final CategoryCommonBiz categoryCommonBiz;
    @NotNull
    private final PaperCommonBiz paperCommonBiz;
    @NotNull
    private final QuestionCommonBiz questionCommonBiz;

    @NotNull
    private final PaperDao dao;
    @NotNull
    private final PaperTitleDao paperTitleDao;
    @NotNull
    private final PaperQuestionDao paperQuestionDao;
    @NotNull
    private final QuestionDao questionDao;
    @NotNull
    private final ExamDao examDao;
    @NotNull
    private final CategoryDao categoryDao;

    /**
     * 试卷信息-分页
     *
     * @param req 试卷信息-分页查询参数
     * @return 试卷信息
     */
    public Result<Page<AdminPaperPageResp>> page(AdminPaperPageReq req) {
        // 处理查询参数
        Set<Long> categoryIds = new HashSet<>();
        if (ObjectUtil.isNotNull(req.getCategoryId())) {
            categoryIds.add(req.getCategoryId());

            if (ObjectUtil.isNull(req.getOnlyCurrentCategory()) || Boolean.FALSE.equals(req.getOnlyCurrentCategory())) {
                List<Long> categoryChildrenIds = categoryCommonBiz.listChildrenIdById(req.getCategoryId());
                if (CollectionUtil.isNotEmpty(categoryChildrenIds)) {
                    categoryIds.addAll(categoryChildrenIds);
                }
            }
        }

        LocalDateTime endGmtCreate = ObjectUtil.isNotNull(req.getEndGmtCreate()) ? req.getEndGmtCreate().plusSeconds(59) : null;

        // 处理查询条件
        LambdaQueryWrapper<Paper> wrapper = new LambdaQueryWrapper<Paper>()
                .eq(ObjectUtil.isNotNull(req.getPaperType()), Paper::getPaperType, req.getPaperType())
                .like(StrUtil.isNotBlank(req.getPaperName()), Paper::getPaperName, req.getPaperName())
                .in(CollectionUtil.isNotEmpty(categoryIds), Paper::getCategoryId, categoryIds)
                .eq(ObjectUtil.isNotNull(req.getRandomCombine()), Paper::getRandomCombine, req.getRandomCombine())
                .eq(ObjectUtil.isNotNull(req.getPublishStatus()), Paper::getPublishStatus, req.getPublishStatus())
                .eq(ObjectUtil.isNotNull(req.getStatus()), Paper::getStatus, req.getStatus())
                .ge(ObjectUtil.isNotNull(req.getBeginGmtCreate()), Paper::getGmtCreate, req.getBeginGmtCreate())
                .le(ObjectUtil.isNotNull(endGmtCreate), Paper::getGmtCreate, endGmtCreate)
                .notIn(CollectionUtil.isNotEmpty(req.getExcludeIds()), Paper::getId, req.getExcludeIds())
                // 排序
                .orderByDesc(Paper::getSort)
                .orderByDesc(Paper::getId);

        // 分页查询
        IPage<Paper> page = dao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        if (ObjectUtil.isNull(page) || CollectionUtil.isEmpty(page.getRecords())) {
            return Result.success(new Page<>());
        }
        Page<AdminPaperPageResp> respPage = PageUtil.transform(page, AdminPaperPageResp.class);
        Set<Long> paperIds = respPage.getList().stream().map(AdminPaperPageResp::getId).collect(Collectors.toSet());

        // 查询引用信息
        Map<Long, Boolean> citeMap = paperCommonBiz.getPaperCiteStatus(paperIds);

        // 处理分页响应信息
        for (AdminPaperPageResp resp : respPage.getList()) {
            resp.setCite(citeMap.getOrDefault(resp.getId(), Boolean.FALSE));
        }
        return Result.success(respPage);
    }

    /**
     * 试卷信息-添加
     *
     * @param req 试卷信息
     * @return 添加结果
     */
    public Result<String> save(AdminPaperSaveReq req) {
        Paper paper = BeanUtil.copyProperties(req, Paper.class);
        paper.setId(IdWorker.getId());
        paper.setRandomCombine(ObjectUtil.isNotNull(req.getRandomCombine()) ? req.getRandomCombine() : Boolean.FALSE);
        paper.setDerangement(ObjectUtil.isNotNull(req.getDerangement()) ? req.getDerangement() : Boolean.FALSE);
        paper.setQuestionDerangement(ObjectUtil.isNotNull(req.getQuestionDerangement()) ? req.getQuestionDerangement() : Boolean.FALSE);
        paper.setOptionDerangement(ObjectUtil.isNotNull(req.getOptionDerangement()) ? req.getOptionDerangement() : Boolean.FALSE);
        paper.setPublishStatus(ObjectUtil.isNotNull(req.getPublishStatus()) ? req.getPublishStatus() : PublishStatusEnum.UNPUBLISHED.getCode());

        RLock lock = redissonClient.getLock(RedisConstant.Exam.PAPER);
        try {
            boolean lockRes = lock.tryLock(RedisConstant.DEFAULT_WAIT_TIME, RedisConstant.DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
            if (!lockRes) {
                log.warn("添加试卷获取锁超时");
                return Result.error(toI18nFail());
            }

            paper.setSort(dao.getMaxSort() + 1);
            if (dao.save(paper)) {
                return Result.success(toI18nSuccess());
            }
            return Result.error(toI18nFail());
        } catch (InterruptedException e) {
            log.error("添加试卷信息失败", e);
            Thread.currentThread().interrupt();
            throw new BaseException(toI18nFail());
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 试卷信息-查看
     *
     * @param id 主键ID
     * @return 试卷信息
     */
    public Result<AdminPaperViewResp> view(Long id) {
        Paper paper = dao.getById(id);
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷信息不存在");
        }
        return Result.success(BeanUtil.copyProperties(paper, AdminPaperViewResp.class));
    }

    /**
     * 试卷信息-详情查看
     *
     * @param id 主键ID
     * @return 试卷信息
     */
    public Result<AdminPaperDetailViewResp> detailView(Long id) {
        Paper paper = dao.getById(id);
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷信息不存在");
        }
        AdminPaperDetailViewResp resp = BeanUtil.copyProperties(paper, AdminPaperDetailViewResp.class);

        // 获取试卷大题、题目
        List<PaperTitle> paperTitleList = paperTitleDao.listByPaperId(paper.getId());
        if (CollectionUtil.isEmpty(paperTitleList)) {
            resp.setPaperTitles(Collections.emptyList());
            return Result.success(resp);
        }
        resp.setPaperTitles(BeanUtil.copyProperties(paperTitleList, AdminPaperDetailViewResp.PaperTitle.class));

        Category category = categoryDao.getById(paper.getCategoryId());
        resp.setCategoryName(ObjectUtil.isNotNull(category) ? category.getCategoryName() : "");

        // 处理试卷标题与试题关联信息
        List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperId(paper.getId());
        if (CollectionUtil.isEmpty(paperQuestionList)) {
            for (AdminPaperDetailViewResp.PaperTitle paperTitle : resp.getPaperTitles()) {
                paperTitle.setPaperQuestions(Collections.emptyList());
            }
            return Result.success(resp);
        }
        Map<Long, List<PaperQuestion>> paperQuestionMap = paperQuestionList.stream().collect(Collectors.groupingBy(PaperQuestion::getTitleId));
        Set<Long> questionIds = paperQuestionList.stream().map(PaperQuestion::getQuestionId).collect(Collectors.toSet());

        // 处理试卷试题信息
        Map<Long, QuestionDetailResp> questionMap = new HashMap<>();

        List<QuestionDetailResp> questionList = questionCommonBiz.listDetailByIds(questionIds);
        if (CollectionUtil.isNotEmpty(questionList)) {
            questionList.forEach(item -> questionMap.put(item.getId(), item));
        }

        // 补充数据
        for (AdminPaperDetailViewResp.PaperTitle titleResp : resp.getPaperTitles()) {
            List<PaperQuestion> paperQuestions = paperQuestionMap.get(titleResp.getId());
            if (CollectionUtil.isEmpty(paperQuestions)) {
                continue;
            }

            // 收集标题下的试题
            List<AdminPaperDetailViewResp.PaperQuestion> paperQuestionRespList = new ArrayList<>();
            for (PaperQuestion paperQuestion : paperQuestions) {
                AdminPaperDetailViewResp.PaperQuestion paperQuestionResp = BeanUtil.copyProperties(paperQuestion, AdminPaperDetailViewResp.PaperQuestion.class);
                paperQuestionResp.setQuestionAnswerIds(Collections.emptyList());
                paperQuestionResp.setOptionCount(0);
                paperQuestionResp.setOptions(Collections.emptyList());

                QuestionDetailResp question = questionMap.get(paperQuestion.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    continue;
                }

                paperQuestionResp.setQuestionTitle(question.getQuestionTitle());
                paperQuestionResp.setQuestionAnswer(question.getQuestionAnswer());
                paperQuestionResp.setQuestionAnalysis(question.getQuestionAnalysis());
                paperQuestionResp.setDifficultyLevel(question.getDifficultyLevel());
                paperQuestionResp.setOptions(BeanUtil.copyProperties(question.getOptions(), AdminPaperDetailViewResp.QuestionOption.class));
                paperQuestionRespList.add(paperQuestionResp);
            }
            // 赋值给标题的试题集合
            titleResp.setPaperQuestions(paperQuestionRespList);
        }
        return Result.success(resp);
    }

    /**
     * 试卷信息-编辑
     *
     * @param req 试卷信息-编辑对象
     * @return 编辑结果
     */
    public Result<String> edit(AdminPaperEditReq req) {
        // 获取校验试卷信息
        Paper paper = dao.getById(req.getId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷信息不存在");
        }

        // 更新试卷信息
        Paper updatePaper = BeanUtil.copyProperties(req, Paper.class);
        updatePaper.setGmtCreate(null);
        updatePaper.setGmtModified(null);
        if (dao.updateById(updatePaper)) {
            return Result.success(toI18nSuccess());
        }
        return Result.error(toI18nFail());
    }

    /**
     * 试卷信息-编辑发布状态
     *
     * @param req 试卷信息-编辑发布状态对象
     * @return 编辑结果
     */
    public Result<String> editPublishStatus(AdminPaperEditPublishStatusReq req) {
        // 获取校验试卷信息
        Paper paper = dao.getById(req.getId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷信息不存在");
        }

        // 更新试卷信息
        Paper updatePaper = BeanUtil.copyProperties(req, Paper.class);
        updatePaper.setGmtCreate(null);
        updatePaper.setGmtModified(null);
        if (dao.updateById(updatePaper)) {
            return Result.success(toI18nSuccess());
        }
        return Result.error(toI18nFail());
    }

    /**
     * 试卷信息-排序
     *
     * @param reqs 试卷信息-排序对象
     * @return 排序结果
     */
    public Result<String> sort(List<AdminPaperSortReq> reqs) {
        // 查询获取试卷信息
        List<Paper> paperList = dao.lambdaQuery()
                .in(Paper::getId, reqs.stream().map(AdminPaperSortReq::getId).collect(Collectors.toSet()))
                .list();
        if (CollectionUtil.isEmpty(paperList)) {
            return Result.success(toI18nSuccess());
        }

        RLock lock = redissonClient.getLock(RedisConstant.Exam.PAPER);
        try {
            boolean lockRes = lock.tryLock(RedisConstant.DEFAULT_WAIT_TIME, RedisConstant.DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
            if (!lockRes) {
                log.warn("试卷排序获取锁超时");
                return Result.error(toI18nFail());
            }

            dao.lambdaUpdate()
                    .in(Paper::getId, paperList.stream().map(Paper::getId).collect(Collectors.toSet()))
                    .setDecrBy(Paper::getSort, Integer.MAX_VALUE)
                    .update();

            // 处理待更新排序的试卷信息
            Map<Long, Integer> sortMap = new HashMap<>();
            reqs.forEach(item -> sortMap.put(item.getId(), item.getSort()));
            List<Paper> updateList = new ArrayList<>();
            for (Paper paper : paperList) {
                Integer sort = sortMap.get(paper.getId());
                if (ObjectUtil.isNull(sort)) {
                    continue;
                }

                Paper updatePaper = new Paper();
                updatePaper.setId(paper.getId());
                updatePaper.setSort(sort);
                updateList.add(updatePaper);
            }

            // 批量更新试卷信息排序
            if (CollectionUtil.isNotEmpty(updateList)) {
                dao.updateBatchById(updateList);
            }
            return Result.success(toI18nSuccess());
        } catch (InterruptedException e) {
            log.error("试卷信息排序失败", e);
            Thread.currentThread().interrupt();
            throw new BaseException(toI18nFail());
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 试卷信息-删除
     *
     * @param id ID主键
     * @return 删除结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<String> delete(Long id) {
        // 获取校验试卷信息
        Paper paper = dao.getById(id);
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷信息不存在");
        }

        // 校验是否被引用
        Boolean citeResult = paperCommonBiz.citeCheck(paper.getId());
        if (Boolean.TRUE.equals(citeResult)) {
            return Result.error(toI18n("resource.paper.delete.cite.error"));
        }

        // 删除试卷标题
        paperTitleDao.deleteByPaperId(id);

        // 删除试卷题目
        paperQuestionDao.deleteByPaperId(id);

        // 删除试卷信息
        dao.removeById(id);
        log.warn("【试卷删除】试卷ID：【{}】-试卷名称：【{}】", paper.getId(), paper.getPaperName());
        return Result.success(toI18nSuccess());
    }

    /**
     * 试卷信息-批量删除
     *
     * @param ids ID主键
     * @return 删除结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<String> deleteBatch(Collection<Long> ids) {
        List<Paper> papers = dao.listByIds(ids);
        if (CollectionUtil.isEmpty(papers)) {
            return Result.error("试卷不存在");
        }
        Set<Long> paperIds = papers.stream().map(Paper::getId).collect(Collectors.toSet());

        Map<Long, Boolean> citeMap = paperCommonBiz.getPaperCiteStatus(paperIds);

        // 需要删除的试卷ID
        Set<Long> deletePaperIds = paperIds.stream().filter(item -> !citeMap.getOrDefault(item, Boolean.FALSE)).collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(deletePaperIds)) {
            return Result.success(toI18nSuccess());
        }

        // 删除试卷标题
        paperTitleDao.deleteByPaperIds(deletePaperIds);

        // 删除试卷题目
        paperQuestionDao.deleteByPaperIds(deletePaperIds);

        // 删除试卷信息
        dao.removeByIds(deletePaperIds);

        for (Paper paper : papers) {
            log.warn("【试卷删除】试卷ID：【{}】-试卷名称：【{}】", paper.getId(), paper.getPaperName());
        }
        return Result.success(toI18nSuccess());
    }

    /**
     * 试卷信息-移动
     *
     * @param req 试题信息-移动对象
     * @return 移动结果
     */
    public Result<String> move(AdminPaperMoveReq req) {
        Category category = categoryDao.getById(req.getCategoryId());
        if (ObjectUtil.isNull(category) || !CategoryTypeEnum.PAPER.getCode().equals(category.getCategoryType())) {
            return Result.error("试卷目录不存在");
        }

        List<Paper> papers = dao.listByIds(req.getIds());
        if (CollectionUtil.isEmpty(papers)) {
            return Result.error("试卷不存在");
        }
        Set<Long> questionIds = papers.stream().map(Paper::getId).collect(Collectors.toSet());

        dao.lambdaUpdate()
                .in(Paper::getId, questionIds)
                .set(Paper::getCategoryId, req.getCategoryId())
                .update();

        for (Paper paper : papers) {
            log.warn("【批量移动试卷】新分类：{}-题目信息：{}", category.getId(), JSONUtil.toJsonStr(paper));
        }
        return Result.success(toI18nSuccess());
    }

    /**
     * 试题信息-引用列表
     *
     * @param id 试题ID
     * @return 引用信息
     */
    public Result<List<AdminPaperCiteListResp>> citeList(Long id) {
        List<AdminPaperCiteListResp> respList = new ArrayList<>();

        // 查看是否被试卷引用
        // 查看是否被试卷引用
        List<Exam> examList = examDao.lambdaQuery()
                .eq(Exam::getPaperId, id)
                .orderByAsc(Exam::getExamType)
                .orderByDesc(Exam::getId)
                .list();
        if (CollectionUtil.isNotEmpty(examList)) {
            for (Exam exam : examList) {
                examList.forEach(item -> respList.add(new AdminPaperCiteListResp(CiteResourceTypeEnum.EXAM.getCode(), item.getId(), item.getExamName())));
            }
        }
        return Result.success(respList);
    }

    /**
     * 试卷信息-组卷
     *
     * @param req 试卷信息-组卷对象
     * @return 组卷结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<String> compose(AdminPaperComposeReq req) {
        // 获取校验试卷信息
        Paper paper = dao.getById(req.getId());
        if (ObjectUtil.isNull(paper)) {
            return Result.error("试卷不存在");
        }

        // 获取原有题目
        List<PaperTitle> titleList = paperTitleDao.listByPaperId(paper.getId());
        Map<Long, PaperTitle> titleMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(titleList)) {
            titleList.forEach(title -> titleMap.put(title.getId(), title));
        }
        List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperId(paper.getId());
        Map<Long, Map<Long, PaperQuestion>> paperQuestionMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(paperQuestionList)) {
            paperQuestionMap.putAll(
                    paperQuestionList.stream()
                            .collect(
                                    Collectors.groupingBy(PaperQuestion::getTitleId,
                                            Collectors.toMap(PaperQuestion::getId, Function.identity()))
                            )
            );
        }

        // 获取题目信息
        Set<Long> questionIds = new HashSet<>();
        for (AdminPaperComposeReq.PaperTitle paperTitle : req.getPaperTitles()) {
            for (AdminPaperComposeReq.PaperQuestion paperQuestion : paperTitle.getPaperQuestions()) {
                questionIds.add(paperQuestion.getQuestionId());
            }
        }
        Map<Long, Question> questionMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(questionIds)) {
            List<Question> questions = questionDao.listByIds(questionIds);
            if (CollectionUtil.isNotEmpty(questions)) {
                questions.forEach(item -> questionMap.put(item.getId(), item));
            }
        }

        Paper updatePaper = new Paper();
        updatePaper.setId(paper.getId());
        updatePaper.setRandomCombine(paper.getRandomCombine());
        updatePaper.setPassPercentage(paper.getPassPercentage());

        List<PaperTitle> savePaperTitles = new ArrayList<>();
        List<PaperQuestion> savePaperQuestions = new ArrayList<>();
        handleCompose(
                updatePaper,
                savePaperTitles,
                savePaperQuestions,
                req.getPaperTitles(),
                questionMap,
                titleMap,
                paperQuestionMap
        );

        // 删除原信息
        paperTitleDao.deleteByPaperId(paper.getId());
        paperQuestionDao.deleteByPaperId(paper.getId());

        // 保存和更新原信息
        paperTitleDao.saveBatch(savePaperTitles);
        paperQuestionDao.saveBatch(savePaperQuestions);
        dao.updateById(updatePaper);
        return Result.success(toI18nSuccess());
    }

    /**
     * 处理更新试卷大题、题目
     *
     * @param paper                    待试卷信息
     * @param savePaperTitles          待保存试卷大题信息
     * @param savePaperQuestions       待保存试卷题目信息
     * @param paperTitleList           试卷大题信息
     * @param questionMap              题目集合
     * @param originalTitleMap         原有大题信息
     * @param originalPaperQuestionMap 原有考试试题信息
     */
    private void handleCompose(
            Paper paper,
            List<PaperTitle> savePaperTitles,
            List<PaperQuestion> savePaperQuestions,
            List<AdminPaperComposeReq.PaperTitle> paperTitleList,
            Map<Long, Question> questionMap,
            Map<Long, PaperTitle> originalTitleMap,
            Map<Long, Map<Long, PaperQuestion>> originalPaperQuestionMap
    ) {

        // 随机组卷设置的单选题分值
        BigDecimal singleChoiceScore = BigDecimal.ZERO;
        // 随机组卷设置的多选题分值
        BigDecimal multipleScore = BigDecimal.ZERO;
        // 随机组卷设置的判断题分值
        BigDecimal judgmentScore = BigDecimal.ZERO;
        // 随机组卷设置的填空题分值
        BigDecimal gapFillingeScore = BigDecimal.ZERO;
        // 随机组卷设置的简答题分值
        BigDecimal shortAnswerScore = BigDecimal.ZERO;

        // 统计随机组卷的总题数
        int questionCount = 0;

        int titleSort = 1;
        for (AdminPaperComposeReq.PaperTitle paperTitle : paperTitleList) {
            // 处理标题ID
            long titleId = ObjectUtil.isNotNull(paperTitle.getId()) && originalTitleMap.containsKey(paperTitle.getId()) ? paperTitle.getId() : IdWorker.getId();

            PaperTitle title = BeanUtil.copyProperties(paperTitle, PaperTitle.class);
            title.setId(titleId);
            title.setPaperId(paper.getId());
            title.setSort(titleSort++);

            Map<Long, PaperQuestion> originalTitleQuestionMap = originalPaperQuestionMap.get(titleId);

            // 处理试卷标题试题关联关系
            int paperQuestionSort = 1;
            // 大题分数
            BigDecimal titleScore = BigDecimal.ZERO;
            // 单选题数量
            Integer singleChoiceCount = 0;
            // 多选题数量
            Integer multipleChoiceCount = 0;
            // 判断题数量
            Integer judgmentCount = 0;
            // 填空题数量
            Integer gapFillingCount = 0;
            // 简答题数量
            Integer shortAnswerCount = 0;

            for (AdminPaperComposeReq.PaperQuestion titleQuestionEditReq : paperTitle.getPaperQuestions()) {
                Question question = questionMap.get(titleQuestionEditReq.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    throw new BaseException(toI18n("resource.paper.question.not.exist"));
                }

                Long id;
                if (ObjectUtil.isNotNull(originalTitleQuestionMap) && originalTitleQuestionMap.containsKey(titleQuestionEditReq.getQuestionId())) {
                    id = originalTitleQuestionMap.get(titleQuestionEditReq.getQuestionId()).getId();
                } else {
                    id = IdWorker.getId();
                }

                PaperQuestion paperQuestion = BeanUtil.copyProperties(titleQuestionEditReq, PaperQuestion.class)
                        .setId(id)
                        .setPaperId(title.getPaperId())
                        .setTitleId(title.getId())
                        .setQuestionId(titleQuestionEditReq.getQuestionId())
                        .setQuestionType(question.getQuestionType())
                        .setSort(paperQuestionSort++);
                // 其他题型
                paperQuestion.setQuestionScore(titleQuestionEditReq.getQuestionScore());

                savePaperQuestions.add(paperQuestion);
                // 统计标题总分
                titleScore = titleScore.add(paperQuestion.getQuestionScore());

                // 统计题型分数
                Integer questionType = paperQuestion.getQuestionType();
                if (QuestionTypeEnum.SINGLE_CHOICE.getCode().equals(questionType)) {
                    singleChoiceScore = paperQuestion.getQuestionScore();
                    singleChoiceCount++;
                } else if (QuestionTypeEnum.MULTIPLE_CHOICE.getCode().equals(questionType)) {
                    multipleScore = paperQuestion.getQuestionScore();
                    multipleChoiceCount++;
                } else if (QuestionTypeEnum.JUDGMENT.getCode().equals(questionType)) {
                    judgmentScore = paperQuestion.getQuestionScore();
                    judgmentCount++;
                } else if (QuestionTypeEnum.GAP_FILLING.getCode().equals(questionType)) {
                    gapFillingeScore = paperQuestion.getQuestionScore();
                    gapFillingCount++;
                } else if (QuestionTypeEnum.SHORT_ANSWER.getCode().equals(questionType)) {
                    shortAnswerScore = paperQuestion.getQuestionScore();
                    shortAnswerCount++;
                }
            }
            if (Boolean.TRUE.equals(paper.getRandomCombine())) {
                // 单选总分
                titleScore = singleChoiceScore.multiply(BigDecimal.valueOf(title.getSingleChoiceCount()))
                        // 多选总分
                        .add(multipleScore.multiply(BigDecimal.valueOf(title.getMultipleChoiceCount())))
                        // 判断总分
                        .add(judgmentScore.multiply(BigDecimal.valueOf(title.getJudgmentCount())))
                        // 填空总分
                        .add(gapFillingeScore.multiply(BigDecimal.valueOf(title.getGapFillingCount())))
                        // 简答总分
                        .add(shortAnswerScore.multiply(BigDecimal.valueOf(title.getShortAnswerCount())));
            } else {
                title.setSingleChoiceCount(singleChoiceCount)
                        .setMultipleChoiceCount(multipleChoiceCount)
                        .setJudgmentCount(judgmentCount)
                        .setGapFillingCount(gapFillingCount)
                        .setShortAnswerCount(shortAnswerCount);
            }
            title.setTitleScore(titleScore);
            savePaperTitles.add(title);

            questionCount += (title.getSingleChoiceCount() + title.getMultipleChoiceCount() + title.getJudgmentCount() + title.getGapFillingCount() + title.getShortAnswerCount());
        }

        // 如果开启随机组卷，则存入组卷题数量
        paper.setQuestionCount(questionCount);
        // 试卷总分
        BigDecimal totalScore = savePaperTitles.stream().map(PaperTitle::getTitleScore).reduce(BigDecimal.ZERO, BigDecimal::add);
        paper.setTotalScore(totalScore);
        // 及格分数
        BigDecimal passScore = totalScore
                .multiply(BigDecimal.valueOf(paper.getPassPercentage()))
                .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
        paper.setPassScore(passScore);
    }
}
